package com.util;

import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * @author: sun.hongliang
 * @create: 2019/07/17 20:02
 */
public class IDGenerator {
    private static ThreadLocal<Random> randomLocal = new ThreadLocal<Random>() {
        public Random initialValue() {
            return new Random();
        }
    };

    private static final char[] char62 = {
            '0', '1', '2', '3', '4',
            '5', '6', '7', '8', '9',
            'a', 'b', 'c', 'd', 'e',
            'f', 'g', 'h', 'i', 'j',
            'k', 'l', 'm', 'n', 'o',
            'p', 'q', 'r', 's', 't',
            'u', 'v', 'w', 'x', 'y',
            'z', 'A', 'B', 'C', 'D',
            'E', 'F', 'G', 'H', 'I',
            'J', 'K', 'L', 'M', 'N',
            'O', 'P', 'Q', 'R', 'S',
            'T', 'U', 'V', 'W', 'X',
            'Y', 'Z'
    };

    private static final Map<Integer, Character> map = new HashMap<>();
    private static final Map<Character, Integer> c2iMap = new HashMap<>();
    private static final Map<Character, String> bMap = new HashMap<>();

    static {
        for (int i = 0; i < 62; i++) {
            map.put(i, char62[i]);
        }

        for (Map.Entry<Integer, Character> li : map.entrySet()) {
            c2iMap.put(li.getValue(), li.getKey());
        }

        bMap.put('0', "00000");
        bMap.put('1', "00001");
        bMap.put('2', "00010");
        bMap.put('3', "00011");
        bMap.put('4', "00100");
        bMap.put('5', "00101");
        bMap.put('6', "00110");
        bMap.put('7', "00111");
        bMap.put('8', "01000");
        bMap.put('9', "01001");
        bMap.put('a', "01010");
        bMap.put('b', "01011");
        bMap.put('c', "01100");
        bMap.put('d', "01101");
        bMap.put('e', "01110");
        bMap.put('f', "01111");
        bMap.put('g', "10000");
        bMap.put('h', "10001");
        bMap.put('i', "10010");
        bMap.put('j', "10011");
        bMap.put('k', "10100");
        bMap.put('l', "10101");
        bMap.put('m', "10110");
        bMap.put('n', "10111");
        bMap.put('o', "11000");
        bMap.put('p', "11001");
        bMap.put('q', "11010");
        bMap.put('r', "11011");
        bMap.put('s', "11100");
        bMap.put('t', "11101");
        bMap.put('u', "11110");
        bMap.put('v', "11111");
    }

    private static BitSet randomPositon(int num) {
        BitSet b = new BitSet(5);
        Random r = getRandom();
        int[] positions = {0, 1, 2, 3, 4};
        for (int i = 0; i < num; i++) {
            int j = r.nextInt(5);
            int temp = positions[j];
            positions[j] = positions[i];
            positions[i] = temp;
        }
        for (int i = 0; i < num; i++) {
            b.set(positions[i]);
        }
        return b;
    }


    private static String bitSet2Str(BitSet bitSet) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 5; i++) {
            sb.append(bitSet.get(i) ? "1" : "0");
        }
        return sb.toString();
    }

    public static String genId(long id) {
        return genIdFor62(to62(id));
    }

    public static String genId(String id) {
        return genId(Long.valueOf(id));
    }


    private static String to62(long id) {
        int[] c62 = new int[5];
        int i = 0;
        while (id != 0) {
            int temp = (int) id;
            int yu = temp % 62;
            c62[i++] = yu;
            id = id / 62;
        }
        StringBuilder sb = new StringBuilder();
        for (int j = c62.length - 1; j >= 0; j--) {
            int t = c62[j];
            sb.append(map.get(t));
        }
        return sb.toString();
    }

    public static String genIdFor62(String id) {
        StringBuilder sb = new StringBuilder();
        BitSet b = randomPositon(2);
        BitSet bb = randomPositon(3);
        sb.append(map.get(Integer.parseInt(bitSet2Str(b), 2)));
        sb.append(map.get(Integer.parseInt(bitSet2Str(bb), 2)));
        int idIndex = 0;
        int strIndex = 0;
        char[] idChar = id.toCharArray();
        String str = radomStr(5);
        char[] strChar = str.toCharArray();
        for (int i = 0; i < 5; i++) {
            if (b.get(i)) {
                sb.append(idChar[idIndex++]);
            } else {
                sb.append(strChar[strIndex++]);
            }
        }
        for (int i = 0; i < 5; i++) {
            if (bb.get(i)) {
                sb.append(idChar[idIndex++]);
            } else {
                sb.append(strChar[strIndex++]);
            }
        }
        return sb.toString();

    }

    private static String radomStr(int num) {
        StringBuilder sb = new StringBuilder();
        Random random = getRandom();
        for (int i = 0; i < num; i++) {
            sb.append(map.get(random.nextInt(62)));
        }
        return sb.toString();
    }

    public static long extractId(String id) {
        try {
            char[] chars = id.toCharArray();
            StringBuilder sb = new StringBuilder(bMap.get(chars[0]));
            sb.append(bMap.get(chars[1]));
            char[] sbc = sb.toString().toCharArray();
            StringBuilder retB = new StringBuilder();
            for (int i = 0; i < sbc.length; i++) {
                if (sbc[i] == '1') {
                    retB.append(chars[2 + i]);
                }
            }
            char[] c62 = retB.toString().toCharArray();
            long result = 0;
            for (int i = 0; i < c62.length; i++) {
                if (c62[i] == '0') {
                    continue;
                }
                result += Math.pow(62, c62.length - 1 - i) * c2iMap.get(c62[i]);
            }
            return result;
        } catch (Exception e) {
            throw new RuntimeException("Wrong wowoid " + id);
        }
    }

    public static String extractIdStr(String id) {
        return String.valueOf(extractId(id));
    }

    private static Random getRandom() {
        return randomLocal.get();
    }
}
